import * as THREE from 'three'

export class MathUtils {
  /**
   * 绕轴旋转(p1绕p2旋转angle)
   * @param p1 旋转向量
   * @param p2 旋转向量
   * @param angle 旋转角度
   * @param axis  旋转轴
   * @returns 旋转后的向量
    */
  static rotateAroundCenter(p1: THREE.Vector3, p2: THREE.Vector3, angle: number, axis: 'X' | 'Y' | 'Z' = 'Z') {
    const _axis = new THREE.Vector3()
    switch (axis) {
      case 'X':
        _axis.set(1, 0, 0)
        break
      case 'Y':
        _axis.set(0, 1, 0)
        break
      case 'Z':
        _axis.set(0, 0, 1)
        break
    }
    const offset = p1.clone().sub(p2)
    const quaternion = new THREE.Quaternion().setFromAxisAngle(_axis, angle)

    return offset.applyQuaternion(quaternion).add(p2)
  }
  static calculateRotation(p2: THREE.Vector3, p1: THREE.Vector3) {
    const px = p2.clone().setX(p1.x)
    const py = p2.clone().setY(p1.y)
    const pz = p2.clone().setX(p1.z)
  
    // 计算 p1 和 p2 之间的向量
    const dx = new THREE.Vector3().subVectors(py, p1)
    const dy = new THREE.Vector3().subVectors(pz, p1)
    const dz = new THREE.Vector3().subVectors(px, p1)
    const xAxis = new THREE.Vector3(1, 0, 0);
    const yAxis = new THREE.Vector3(0, 1, 0);
    const zAxis = new THREE.Vector3(0, 0, 1);
  
    // 计算在 X 轴上的夹角
    const angleX = dx.angleTo(xAxis);
  
    // 计算在 Y 轴上的夹角
    const angleY = dy.angleTo(yAxis);
  
    // 计算在 Z 轴上的夹角
    const angleZ = dz.angleTo(zAxis);

    return new THREE.Euler(angleX, angleY, angleZ)
  }
  static calculateRotationAroundLine(p1: THREE.Vector3, p2: THREE.Vector3, p3: THREE.Vector3): number {
    // 计算 p1 和 p2 线段的方向向量
    const lineDirection = new THREE.Vector3().subVectors(p2, p1).normalize();

    // 计算 p3 在 p1 和 p2 线段上的投影点
    const p3ToP1 = new THREE.Vector3().subVectors(p3, p1);
    const projectionLength = p3ToP1.dot(lineDirection);
    const projectionPoint = new THREE.Vector3().addVectors(p1, lineDirection.clone().multiplyScalar(projectionLength));

    // 计算 p3 到投影点的向量
    const p3ToProjection = new THREE.Vector3().subVectors(p3, projectionPoint);

    // 计算 p3 到投影点的向量与 p1 到 p2 线段方向向量的夹角
    const angle = p3ToProjection.angleTo(lineDirection);

    return angle;
  }
}

export default class Utils {
  // 随机颜色值
  static getRandomColor(excludeColor: Partial<THREE.Color> = new THREE.Color(255, 255, 255)) {
    const { r: _r, g: _g, b: _b } = excludeColor
    let r, g, b;
    do {
      // 生成随机的RGB值
      r = Math.floor(Math.random() * 256);
      g = Math.floor(Math.random() * 256);
      b = Math.floor(Math.random() * 256);
    } while (excludeColor && r === _r && g === _g && b === _b); // 确保跟传入的颜色值都不相等

    // 转换为十六进制格式
    const color = `0x${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
    return new THREE.Color(color);
  }
  // 将屏幕坐标转换为webgl坐标
  static screenToWebGL(screenX: number, screenY: number, dom: HTMLElement, dimension: 2 | 3 = 2) {
    const rect = dom?.getBoundingClientRect();
    const width = rect?.width || innerWidth;
    const height = rect?.height || innerHeight;
    const x = ((screenX - rect?.x || 0) / width) * 2 - 1;
    const y = -((screenY - rect?.y || 0) / height) * 2 + 1;
    return dimension == 2 ? new THREE.Vector2(x, y) : new THREE.Vector3(x, y, 0);
  }
  /**
   * @description 生成唯一 uuid
   * @return string
   */
  static generateUUID() {
    if (typeof crypto === 'object') {
      if (typeof crypto.randomUUID === 'function') {
        return crypto.randomUUID()
      }
      if (
        typeof crypto.getRandomValues === 'function' &&
        typeof Uint8Array === 'function'
      ) {
        const callback = (c: any) => {
          const num = Number(c)
          return (
            num ^
            (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))
          ).toString(16)
        }
        return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback)
      }
    }
    let timestamp = new Date().getTime()
    let performanceNow =
      (typeof performance !== 'undefined' &&
        performance.now &&
        performance.now() * 1000) ||
      0
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
      let random = Math.random() * 16
      if (timestamp > 0) {
        random = (timestamp + random) % 16 | 0
        timestamp = Math.floor(timestamp / 16)
      }
      else {
        random = (performanceNow + random) % 16 | 0
        performanceNow = Math.floor(performanceNow / 16)
      }
      return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16)
    })
  }
}